<template>
  <div class="setup" :style="showAnimation">
    <o-context-menu
      :list="menuList"
      :callback="contextMenuCallback"
      :displayPosition="showPosition"
      width="100px"
      v-model="showContextMenu"
      @compBoxInfo="globalContextMenu"
      @on-select="onContextSelect"
    ></o-context-menu>
    <div class="back"></div>
    <div class="front">
      <div class="box">
        <div class="header">
          <div class="wytoolbar">
            <oSimpleToolbar
              id="wytoolbarid"
              :list="G_list"
              :compBoxInfo="GT_compBoxInfo"
              @click="btnClick"
            />
          </div>

          <!-- setup关闭按钮 -->
          <div class="close-btn" @click="close">
            <Icon type="ios-close" />
          </div>
        </div>

        <div class="main">
          <div class="main-top">
            <oCardBox
              position="top"
              :height="120"
              @compBoxInfo="mainTopCompBoxInfo"
            >
              <components-panel
                :isShow="status.isShow"
                :draggable="compPanelDraggable"
                :compBoxInfo="GT_compBoxInfo"
              ></components-panel>
            </oCardBox>
          </div>

          <div class="main-left" v-show="switcher.main !== 0">
            <oCardBox position="left" :width="230" :isExpand="true">
              <!-- layuot选择面板 -->
              <layouts-panel
                :isShow="status.isShow"
                :layoutID="layout.id"
                :draggable="layoutsPanelPaneldraggable"
                @on-layout-change="layoutChange"
                :compBoxInfo="GT_compBoxInfo"
              ></layouts-panel>
            </oCardBox>
          </div>

          <div
            class="main-mid"
            @drop="compDrop"
            @dragover="allowDrop"
            @dragenter="dragEnter"
            @dragleave="dragLeave"
            @dragstart="onDragStart"
          >
            <!-- layout editor -->
            <Layout-editor
              v-if="layout.isSelected"
              :config="layoutisadd ? editorconfig : layout.current"
              :compBoxInfo="GT_compBoxInfo"
            ></Layout-editor>
            <!-- 右边编辑画面 -->
          </div>

          <div class="main-right" v-show="cell.isSelected">
            <Tabs
              type="card"
              name="attr"
              :animated="false"
              :value="switcher.attr"
              @on-click="handleAttrClick"
            >
              <!-- 选择组件tab -->
              <TabPane
                tab="attr"
                :label="$srv.getI18nByPath('oSetupPanel.component')"
                name="attr_0"
                :index="1"
              >
                <setup-attr-current
                  :isChange="status.isChange"
                  @set-main-component="setMainComponent"
                  :compBoxInfo="GT_compBoxInfo"
                ></setup-attr-current>
              </TabPane>
              <!-- 组件明细tab -->
              <!-- $srv.getI18nByPath('oSetupPanel.componentdetails') -->
              <TabPane
                v-if="
                  $srv.utils.get(component, 'current', null) &&
                    $srv.utils.get(
                      currentCompPanelConf,
                      'attrDetail.currentTab.isShow',
                      false
                    )
                "
                :disabled="
                  $srv.utils.get(component, 'current', null) == null ||
                    $srv.utils.get(
                      currentCompPanelConf,
                      'attrDetail.currentTab.disabled',
                      false
                    )
                "
                :label="$srv.getI18nByPath('oSetupPanel.componentdetails')"
                tab="attr"
                name="attr_1"
                :index="2"
              >
                <setup-attr-detail
                  @save-changes="saveChanges"
                  :compBoxInfo="GT_compBoxInfo"
                  :isChange="status.isChange"
                ></setup-attr-detail>
              </TabPane>
              <!-- 字段tab -->
              <TabPane
                v-if="
                  $srv.utils.get(component, 'current', null) &&
                    $srv.utils.get(
                      currentCompPanelConf,
                      'attrLibFeild.currentTab.isShow',
                      false
                    )
                "
                :label="$srv.getI18nByPath('oSetupPanel.field')"
                tab="attr"
                name="attr_2"
                :index="3"
              >
                <setup-attr-libfield
                  @save-changes="saveChanges"
                  :tabChange="switcher.attr"
                  :isChange="status.isChange"
                  ref="libfield"
                  :compBoxInfo="GT_compBoxInfo"
                ></setup-attr-libfield>
              </TabPane>

              <TabPane
                v-if="
                  component.current !== null &&
                    $srv.utils.get(
                      currentCompPanelConf,
                      'attrMenu.currentTab.isShow',
                      false
                    )
                "
                :disabled="
                  $srv.utils.get(component, 'current', null) === null ||
                    $srv.utils.get(
                      currentCompPanelConf,
                      'attrMenu.currentTab.disabled',
                      true
                    ) === true ||
                    $srv.utils.get(
                      component,
                      'current.config.status',
                      'def'
                    ) !== 'main'
                "
                :label="$srv.getI18nByPath('oSetupPanel.menu')"
                tab="attr"
                name="attr_3"
                :index="99"
              >
                <setup-attr-menu
                  @save-changes="saveChanges"
                  :isChange="status.isChange"
                  :compBoxInfo="GT_compBoxInfo"
                ></setup-attr-menu>
              </TabPane>
            </Tabs>
          </div>
        </div>
      </div>
    </div>
  </div>
</template>
<script lang="ts">
import { N_oSimpleToolbar } from '../../toolbar/o-simple-toolbar/types';
import {
  COMP_BASE_DATA_NAV_NAME,
  COMP_BASE_GRID_NAME,
  COMP_BASE_PANEL_NAME,
  COMP_BASE_TAB_NAME,
  COMP_BASE_VIEW_NAME,
  COMP_CBASE_FORM_NAME,
  COMP_CLONE_BASE_PANEL_NAME,
  COMP_DATE_NAV_NAME,
  COMP_GRAPH_NAME,
  COMP_MULTI_FORM_NAME,
  COMP_REF_BASE_GRID_NAME,
  COMP_REF_COMP_NAME,
  COMP_REF_GRAPH_NAME,
  COMP_REPORT_CHART_NAME,
  COMP_TREE_GRID_NAME,
  ComponentsPanel,
  I_command,
  LayoutEditor,
  LayoutsPanel,
  N_oSetupPanel,
  SetupAttrCurrent,
  SetupAttrDetail,
  SetupAttrLibfield,
  SetupAttrMenu,
  T_command,
  T_srv,
  Tsrv,
  VtoolbarState,
  defaultToolbarList,
  ifs,
  ts,
  tvs,
  vue
} from './config';

@T_srv.comp({
  components: {
    LayoutsPanel,
    ComponentsPanel,
    SetupAttrCurrent,
    SetupAttrMenu,
    SetupAttrLibfield,
    SetupAttrDetail,
    LayoutEditor
  }
})
export default class oSetupPanel extends vue {
  @T_srv.prop({
    type: Object,
    default: () => {}
  })
  config: any;

  @T_srv.prop({
    type: Number,
    default: 0
  })
  id: number;

  /* 父组件compBoxInfo */
  @T_srv.prop({
    required: false,
    type: Object,
    default: null
  })
  compBoxInfo: ifs.I_compBoxInfo;

  /* 组件名 */
  private G_compName: string = 'oSetupPanel';

  /* 组件箱信息对象 */
  private GT_compBoxInfo: ifs.I_compBoxInfo;

  /* 组件箱对象 */
  private GT_compBox: ifs.I_compBox;

  /* 组件箱识别ID */
  private G_compID: string;

  private showAnimation: any = {};

  private loading = false;

  public showContextMenu: boolean = false;

  public showPosition = { clientX: 100, clientY: 100 };

  public menuList: Array<any> = [];

  public contextMenuCallback: Function = null;

  public layoutsPanelPaneldraggable: boolean = true;

  public compPanelDraggable: boolean = true;

  private _config: any = null;

  private status = {
    isShow: false, // 是否渲染组件，未打开则不渲染
    isChange: false // 是否变化了cell的选择，发送给其他组件信息
  };

  private switcher = {
    main: 1, // 顶部按钮的index，0为关闭所有，1为开启布局，2为开启组件
    attr: 'attr_0' // 属性栏的切换
  };

  private layout = {
    id: '', // 当前layoutID
    isSelected: false, // 是否选择了layout
    current: null // 正在编辑的layout的配置
  };

  private cell = {
    id: '', // 当前cellID
    isSelected: false // 是否选择了cell
  };

  private component = {
    current: null // 正在编辑的组件配置
  };

  // 编辑的所有components的配置
  private configStore = {
    all: {},
    stored: []
  };

  private mainLayoutName: any = '';

  private recoverConfig: any = {}; // 用于配置数据恢复的中间变量

  private GT_command: I_command;

  private G_toolMap = new Map();

  private G_list: Array<N_oSimpleToolbar.I_Conf> = defaultToolbarList();

  private editorconfig: any = {}; // 用于修改模板clr

  private layoutisadd: boolean = false; // 是否是模板新增clr

  private layouts: Array<any> = [];

  /**
   * 获取当前组件的类
   */
  get currentCompClass() {
    for (const key in this.GT_command.Tsetup) {
      if (
        !Tsrv.utils.isEmpty(this.GT_command.Tsetup.component.current) &&
        key === this.GT_command.Tsetup.component.current.name
      ) {
        return this.GT_command.Tsetup[key];
      }
    }
    return null;
  }

  /**
   * 获取当前组件的面板配置
   */
  get currentCompPanelConf() {
    for (const key in this.GT_command.Tsetup) {
      if (key === COMP_BASE_TAB_NAME) {
        continue;
      }
      if (
        !Tsrv.utils.isEmpty(this.GT_command.Tsetup.component.current) &&
        key === this.GT_command.Tsetup.component.current.name &&
        this.GT_command.Tsetup[key]?.GetCompPanelConf()
      ) {
        return this.GT_command.Tsetup[key].GetCompPanelConf();
      }
    }
    return null;
  }

  beforeDestroy() {}

  destroyed() {
    /* 注册组件销毁 */
    this.GT_compBox.removeCompMergeName(this.G_compID, this.G_compName);

    /* 如果父组件传入的compBoxInfo则删除本文件产生的引用,反之是本组件产生的ID统一删除 */
    if (Tsrv.utils.isEmpty(this.compBoxInfo)) {
      this.GT_compBox.removeCompsWithCompID(this.G_compID);
    } else {
      this.GT_compBox.removeCompsMergeName(this.G_compID, [
        `'${this.G_compName}'`
      ]);
    }
    this.GT_command.handleEditLock(this._config?.lockInfo, 'unLock');
  }

  created() {
    this._config = Tsrv.utils.clone(this.config, true);

    // 如果父组件没有传参，那就产生一个
    this.GT_compBoxInfo = Tsrv.getCompInfo(this.compBoxInfo);
    this.G_compID = this.GT_compBoxInfo.compID;
    this.GT_compBox = this.GT_compBoxInfo.TcompBox;
    this.GT_command = new T_command(this, this.GT_compBoxInfo);
    this.GT_compBox.addCompMergeName(
      this.G_compID,
      'oSetupPanelCMD',
      this.GT_command
    );
    this.$emit('compBoxInfo', this.GT_compBoxInfo);
    this.GT_compBox.addCompMergeName(this.G_compID, this.G_compName, this);
    this.G_list.forEach(o => this.G_toolMap.set(o.key, o));
  }

  mounted() {
    this.findLayoutInit() // 这里就保障layout能抓到数据后执行show
      .then(b => {
        if (b) {
          // 在修改功能时this._config = {config:any,lockInfo:{funID:string,row:any}}
          this.show(this._config?.config ? this._config.config : this._config);
        } else {
          this.$msg(Tsrv.getI18nByValue('未抓取到数据'));
        }
      })
      .catch(r => {
        throw new Error(r);
      });
    this.initEvent();
    this.GT_command.handleEditLock(this._config?.lockInfo, 'lock').then(b => {
      if (!b) {
        this.close();
      }
    });
  }

  private initEvent() {
    // 监听页面的打开事件
    // this.$bus.$on("show-setup", (config = null) => {
    //   this.show(config);
    // });
  }

  // cell选择
  public setupOnCellSelected(cellID: string) {
    this.handleCellSelected(cellID);
  }

  // cell重置
  public setupResetCell() {
    this.handleResetCurrentCell();
  }

  // cell放入事件
  public setupOnCellFilled(type: any, componentID: any, cellID: any) {
    this.handleCellFilled(type, componentID, cellID);
  }

  // 修改功能初始化用
  public initCompIDList(tabsList: Array<N_oSetupPanel.I_editorTabsList>) {
    if (!Tsrv.noValue(tabsList) && Array.isArray(tabsList)) {
      tabsList.forEach((el, index) => {
        this.updateCompIDList(el.tabsID, index, tabsList);
      });
    }
  }

  // 更新所有compID汇总
  public updateCompIDList(
    tabsID: string, // tabs组件ID
    currTabsIdx: number, // 当前tabslist 下标
    tabsList: N_oSetupPanel.I_editorTabsList[]
  ) {
    const chk = (): boolean => {
      if (
        !Tsrv.noValue(tabsList) &&
        tabsList.filter(el => Tsrv.noValue(el.cellID)).length > 0
      ) {
        return false;
      }
      const cellID = !Tsrv.noValue(this.cell.id)
        ? this.cell.id
        : tabsList[currTabsIdx].cellID;
      if (Tsrv.noValue(cellID)) {
        return false;
      }
      return true;
    };

    if (!chk()) {
      return;
    }
    // 重置数据
    this.GT_command.Tsetup.cell.structure[tabsID] = [];
    // 存入
    tabsList.forEach(el => {
      if (!Tsrv.noValue(el.compName)) {
        this.GT_command.Tsetup.cell.structure[tabsID].push(el.cellID);
      }
    });
    this.GT_command.Tsetup.cell.list = [];
    // 至少有一个组件存在
    if (!Tsrv.noValue(tabsList[currTabsIdx].compName)) {
      // 如果存在BaseTab
      if (!Tsrv.noValue(tabsList[currTabsIdx].label)) {
        this.handleTabsList(tabsID, tabsList);
      } else {
        // 存入 组件关联
        this.GT_command.Tsetup.cell.structure[tabsID].push(tabsID);
      }
    }
    this.setCellList();
    this.status.isChange = !this.status.isChange;
  }

  private setCellList() {
    for (const key in this.GT_command.Tsetup.cell.structure) {
      const list = this.GT_command.Tsetup.cell.structure[key];
      if (Tsrv.noValue(list)) {
        continue;
      }
      // baseTab不能作为组件关联选单
      const compList = list.filter(li => {
        if (Tsrv.noValue(this.configStore?.all[li])) {
          return false;
        }
        return this.configStore?.all[li].name !== COMP_BASE_TAB_NAME;
      });
      if (Tsrv.noValue(compList)) {
        continue;
      }
      // 组件关联
      this.GT_command.Tsetup.cell.list = this.GT_command.Tsetup.cell.list.concat(
        compList
      );
      this.GT_command.Tsetup.cell.list = [
        ...new Set(this.GT_command.Tsetup.cell.list)
      ];
    }
  }

  private handleTabsList(
    tabsID: string,
    tabsList: Array<N_oSetupPanel.I_editorTabsList>
  ) {
    if (this.configStore.all[tabsID].config.panels) {
      // 备份一下容器的项
      const containerPanels = this.configStore.all[tabsID].config.panels.filter(
        el => !Tsrv.noValue(el.content?.config)
      );
      // 清空数组
      this.configStore.all[tabsID].config.panels = [];

      tabsList.forEach((el, idx) => {
        if (el.compName !== '' && !el.isContainer) {
          this.configStore.all[tabsID].config.panels.push({
            label: el.label,
            content: `${el.cellID}`
          });
        }
      });
      // 补充容器的项
      if (!Tsrv.noValue(containerPanels)) {
        this.configStore.all[tabsID].config.panels.push(...containerPanels);
      }
      // 排序
      this.configStore.all[tabsID].config.panels.sort((a, b) => {
        // 提取a和b的对比键值
        const getKeyValue = obj => {
          if (typeof obj.content === 'string') {
            return obj.content; // 如果ele是字符串，直接返回
          }
          if (typeof obj.content === 'object' && obj?.content?.cellID) {
            return obj.content.cellID; // 如果ele是对象且含有cellID属性，返回cellID的值
          }
          return ''; // 其他情况返回空字符串或适当的默认值
        };

        const keyA = getKeyValue(a);
        const keyB = getKeyValue(b);

        // 对比键值并进行排序
        if (keyA < keyB) {
          return -1;
        }
        if (keyA > keyB) {
          return 1;
        }
        return 0;
      });
    }
  }

  // 保存按钮
  private save() {
    if (!this.loading) {
      this.transformConfigToJson()
        .then((config: any) => {
          this.verifyBeforeSaving(config)
            .then(verify => {
              if (verify) {
                const msg = this.$Message.loading({
                  content: Tsrv.getI18nByPath('prompt.saving') as string,
                  duration: 0
                });

                if (!Tsrv.utils.isJSONString(JSON.stringify(config))) {
                  this.$msg(Tsrv.getI18nByValue('数据不合法无法更新'));
                  this.$Message.destroy();
                  return;
                }
                this.loading = true;
                Tsrv.standardPostReq<ifs.I_setDataRes, any>(
                  config,
                  Tsrv.url.setFunConf
                )
                  .then(([res, m]) => {
                    this.loading = false;
                    this.$Message.destroy();
                    this.$msg(m ?? res.msg);

                    if (!res.success) {
                      return;
                    }
                    const data = res.data;
                    ['_sys_fun'].forEach(el => {
                      this.$bus.$emit(el, {
                        type: 'refresh',
                        listenType: 'default'
                      });
                    });
                    this.$bus.$emit('refreshLayout', {
                      name: this.mainLayoutName,
                      config: config.layout
                    });
                    this.mainLayoutName = [];
                    this.close();
                    // this.upLayoutfvalue(); // 更新模板表中f_value的值，使用模板次数
                    this.GT_command.setToolbarState({
                      name: VtoolbarState.SAVE,
                      state: true
                    });
                  })
                  .catch(err => {
                    this.loading = false;
                    this.$Message.destroy();
                    throw new Error(err);
                  });
              }
            })
            .catch(err => {
              this.loading = false;
              this.$Message.destroy();
              this.$msg(err);
            });
        })
        .catch(msg => {
          this.loading = false;
          this.$Message.destroy();
          this.$msg(msg);
        });
    }
  }

  // 生成最终json
  private transformConfigToJson() {
    return new Promise((resolve, reject) => {
      // 拷贝config
      const config = Tsrv.utils.clone(this.configStore.all, true);

      const libfieldConfig = [];
      const menuConfig = {};
      this.mainLayoutName = '';
      const res = this.handleConfig(config, libfieldConfig, menuConfig);
      if (res.isErr()) {
        reject(res.errmsg);
      }
      // 处理自定义事件
      this.GT_command.handleCustMessageConfig(config);

      // 将当前layout字符串化
      let layoutConfigStr = JSON.stringify(this.layout.current);
      // tabs装入layout
      Object.keys(config).forEach(key => {
        const rule = `"name":"${key}","type":"comp","config":{}}`;
        layoutConfigStr = layoutConfigStr.replace(
          rule,
          JSON.stringify(config[key]).slice(1)
        );
      });
      const layoutId =
        this.GT_compBox.getCompMergeName(
          this.G_compID,
          'LayoutsPanel'
        ).layoutused.find(e => e.sysfunid === this.layout.id)?.id ??
        this.layout.id;
      // layout重新json化
      const layoutConfig = JSON.parse(layoutConfigStr);
      // 返回最终服务器保存数据
      const megaConfig = {
        layout: layoutConfig,
        funs: this.getFuns(libfieldConfig),
        menu: menuConfig,
        // layoutID: this.layout.id
        layoutID: layoutId
      };
      if (!this.verifyMenuConfig(megaConfig.menu)) {
        reject('-50003,FE,0,');
      }
      this.verifyFuns(megaConfig.funs)
        .then(() => {
          resolve(megaConfig);
        })
        .catch(msg => {
          reject(msg);
        });
    });
  }

  private getFuns(p: Array<any>): Array<any> {
    const fncf = p.filter(el => el.type !== COMP_BASE_DATA_NAV_NAME);

    return Tsrv.utils.clone(fncf, true);
  }

  private handleConfig(config, libfieldConfig, menuConfig): ifs.I_return {
    const result: ifs.I_return = new ts.T_return();

    for (const key of Object.keys(config)) {
      if (config[key].name === 'Graph') {
        this.handleGraphConfig(config[key]);
      }
      if (Tsrv.noValue(config[key])) {
        continue;
      }

      // 如果为主要cell， 且有tabs
      if (key.indexOf('-') < 0 && !Tsrv.noValue(config[key]?.config?.panels)) {
        config[key]['cellID'] = key;
        for (const el of config[key].config.panels) {
          // el.content是挂组件还是容器一并处理
          const err = this.GT_command.setTabsComps(
            el,
            config,
            libfieldConfig,
            menuConfig,
            key
          );
          if (err.isErr()) {
            throw new Error(err.errmsg);
          }
        }
        // 为SERVER准备funs
        const _libfieldConfig = libfieldConfig.concat(
          this.configToLibfield(config[key], config, libfieldConfig)
        );
        libfieldConfig.splice(0);
        libfieldConfig.push(..._libfieldConfig);
      } else if (key.indexOf('-') >= 0) {
        continue;
      } else {
        // 如果为主要cell，且没有tabs
        config[key]['cellID'] = key;
        // 设置功能为完成状态，不再可修改功能名
        if (config[key].config.isCreated) {
          config[key].config.isCreated = true;
        }
        // 验证config
        const verifyConfRes = this.verifyConfig(config[key].config);
        if (verifyConfRes.isErr()) {
          return verifyConfRes;
        }
        // 设置messageConfig
        if (config[key].config.messageConfig) {
          const res = this.configToMessageConfig(
            config[key].config.messageConfig,
            config
          );
          if (res.isErr()) {
            return res;
          }
        }
        // 为SERVER准备funs
        if (config[key].config) {
          const _libfieldConfig = libfieldConfig.concat(
            this.configToLibfield(config[key], config, libfieldConfig)
          );
          libfieldConfig.splice(0);
          libfieldConfig.push(..._libfieldConfig);
        }
        // 为SERVER准备menu
        if (config[key].config.status === 'main') {
          if (config[key].menu !== undefined) {
            // menuConfig = this.configToMenu(config[key]);
            Object.assign(menuConfig, this.configToMenu(config[key]));
          }
        }
      }
    }
    for (const key of Object.keys(config)) {
      if (key.indexOf('-') >= 0) {
        delete config[key];
      }
    }
    return result;
  }

  private handleGraphConfig(config) {
    if (Tsrv.utils.noValue(config?.config?.tableColumns)) {
      return;
    }
    if (Tsrv.utils.noValue(config?.config?.nodeColumns)) {
      config.config['nodeColumns'] = [];
    }
    config.config.nodeColumns = [];
    config.config.tableColumns.forEach((v, idx, arr) => {
      if (v?.nodeData) {
        config.config.nodeColumns.push(v);
      }
    });
  }

  private verifyFuns(funs: any) {
    return new Promise((resolve, reject) => {
      if (funs.length > 0) {
        for (let i = 0; i < funs.length; i++) {
          if (Tsrv.utils.isEmpty(funs[i].name)) {
            reject('-50005,FE,0,');
          }
        }
        resolve(true);
      } else {
        reject('-50004,FE,0,');
      }
    });
  }

  private verifyMenuConfig(menu: any) {
    if (!Tsrv.utils.isEmpty(menu)) {
      if (
        !Tsrv.utils.isEmpty(menu.navName) &&
        !Tsrv.utils.isEmpty(menu.group)
      ) {
        return true;
      }
    }
    return false;
  }

  // 设置messageConfig
  public verifyConfig(config): ifs.I_return {
    const result = new ts.T_return();
    if (!Tsrv.noValue(config?.formPanels)) {
      for (const panel of config.formPanels) {
        const formColumns = panel.content.formColumns;
        if (Tsrv.noValue(formColumns)) {
          result.errmsg = `“${Tsrv.getI18nBySrv(panel.label)}”表单字段为空！`;
          return result;
        }
      }
    }
    return result; // 如果没有错误，返回成功结果
  }

  /**
   * 功能配置保存前验证
   */
  private verifyBeforeSaving(config): Promise<boolean> {
    return new Promise((resolve, reject) => {
      const error = new ts.T_return();
      const warnInfo: Array<string> = [];
      // const funs = config?.funs;
      // const layoutID = config?.layoutID;
      // const menu = config?.menu;
      const layout = config?.layout;
      // 验证funs
      // if (!Tsrv.noValue(funs)) {}
      // 验证layoutID
      // if (Tsrv.noValue(layoutID)) {
      //   error.error(Tsrv.getI18nByValue('布局ID为空，请联系开发发人员'));
      //   return;
      // }
      // 验证menu
      // if (!Tsrv.noValue(menu)) {}
      // 验证layout
      if (!Tsrv.noValue(layout)) {
        this.verifyConfigLayout(layout, error, warnInfo);
      }

      if (error.isErr()) {
        // 报错
        this.$msg(error.errmsg);
        this.$Message.destroy();
        resolve(false);
      } else if (warnInfo.length > 0) {
        // 警告提示
        const wornings = warnInfo.map((el, idx) => {
          return `<p>${idx + 1}、${Tsrv.getI18nByValue(el)}</p>`;
        });
        const worningContext = wornings.join('');
        this.$Modal.confirm({
          title: Tsrv.getI18nByValue('警示'),
          content: `${worningContext}<p style="font-weight: 700; color: #57a3f3;margin-top: 5px;}">${Tsrv.getI18nByValue(
            '● 是否继续保存？'
          )}</p>`,
          okText: Tsrv.getI18nByValue('是'),
          cancelText: Tsrv.getI18nByValue('否'),
          onOk: () => {
            resolve(true);
          },
          onCancel: () => {
            resolve(false);
          }
        });
      } else {
        resolve(true);
      }
    });
  }

  /**
   * @description:  验证layout
   * @param layout
   * @param error
   * @param warnInfo
   * @returns void
   */
  private verifyConfigLayout(
    layout: any,
    error: ts.T_return,
    warnInfo: Array<string>
  ) {
    // 验证组件配置config
    const verifyComp = (
      name: any,
      config: any,
      err: ts.T_return,
      warnings: Array<string>
    ) => {
      if (name === N_oSetupPanel.N_comps.COMP_NAME.BASE_TAB) {
        //
      } else {
        if (name === N_oSetupPanel.N_comps.COMP_NAME.BASE_GRID) {
          // 验证字段相关
          this.verifyFieldConfig(
            name,
            config,
            config.tableColumns,
            err,
            warnings
          );
        }
        if (name === N_oSetupPanel.N_comps.COMP_NAME.BASE_VIEW) {
          // 验证字段相关
          this.verifyFieldConfig(
            name,
            config,
            config.tableColumns,
            err,
            warnings
          );
        }
        if (name === N_oSetupPanel.N_comps.COMP_NAME.REF_BASE_GRID) {
          // 验证字段相关
          this.verifyFieldConfig(
            name,
            config,
            config.tableColumns,
            err,
            warnings
          );
        }
        if (name === N_oSetupPanel.N_comps.COMP_NAME.BASE_PANEL) {
          // 表头：
          // 验证表头字段相关
          const column = [];
          config.formPanels.forEach(formPanel => {
            column.push(...formPanel?.content?.formColumns);
          });
          this.verifyFieldConfig(name, config, column, err, warnings);
          // 表体：
          // 验证表体字段相关
        }
        if (name === N_oSetupPanel.N_comps.COMP_NAME.C_BASE_FORM) {
          // 验证字段相关
          const column = [];
          config.formPanels.forEach(formPanel => {
            column.push(...formPanel?.content?.formColumns);
          });
          this.verifyFieldConfig(name, config, column, err, warnings);
        }
        if (name === N_oSetupPanel.N_comps.COMP_NAME.GRAPH) {
          //
        }
        // ...
        // TreeGrid、DateNav、RefComp、MultiForm、RefGraph、ReportChart、CloneBasePanel
      }
    };
    if (!Tsrv.noValue(layout?.type)) {
      const config = layout?.config;
      if (Tsrv.noValue(config)) {
        return;
      }
      if (layout.type === 'comp') {
        verifyComp(layout.name, config, error, warnInfo);
      } else if (layout.type === 'layout') {
        this.verifyConfigLayout(config?.first, error, warnInfo);
        this.verifyConfigLayout(config?.second, error, warnInfo);
      }
    }
  }

  /**
   * 检查字段配置项
   * @param config
   * @param columns
   * @param err
   * @param warnings
   */
  private verifyFieldConfig(
    compName: N_oSetupPanel.N_comps.COMP_NAME,
    config: any,
    columns: Array<any>,
    err: ts.T_return,
    warnings: Array<string>
  ) {
    // 验证一：多对一字段m2o、下拉字段slc 不能作为 描述字段
    // 验证二：描述字段 尽量添加 唯一索引
    columns.forEach(column => {
      if (column?.isDes) {
        if (!Tsrv.noValue(column.selectionID)) {
          warnings.push(
            Tsrv.getI18nByValue(
              `“${Tsrv.getI18nBySrv(
                config?.name
              )}”— 多对一字段不建议作为描述字段`
            )
          );
        }
        if (!Tsrv.noValue(column.many2one?.toTable)) {
          warnings.push(
            Tsrv.getI18nByValue(
              `“${Tsrv.getI18nBySrv(config?.name)}”— 下拉字段不建议作为描述字段`
            )
          );
        }
        if (config?.uniqueIndexes) {
          let uniqueIndexes = config.uniqueIndexes;
          if (compName === N_oSetupPanel.N_comps.COMP_NAME.BASE_PANEL) {
            uniqueIndexes = config.formPanels[0].content.uniqueIndexes;
          }
          uniqueIndexes.length === 0 &&
            warnings.push(
              Tsrv.getI18nByValue(
                `“${Tsrv.getI18nBySrv(
                  config?.name
                )}”— 描述字段建议添加唯一索引，可使数据展示较为明确`
              )
            );
          if (uniqueIndexes.length > 0) {
            let isUniqueIndex = false;
            uniqueIndexes.forEach((uniqueIndex: Array<any>) => {
              uniqueIndex.length === 1 &&
                uniqueIndex[0]?.name === column.field &&
                (isUniqueIndex = true);
            });
            if (!isUniqueIndex) {
              warnings.push(
                Tsrv.getI18nByValue(
                  `“${Tsrv.getI18nBySrv(
                    config?.name
                  )}”— 描述字段建议添加唯一索引，可使数据展示较为明确`
                )
              );
            }
          }
        }
      }
    });
  }

  // 设置messageConfig
  public configToMessageConfig(message, config): ifs.I_return {
    const result = new ts.T_return();
    const emitActions = message.emitActions;
    // 将search和refresh中cellID替换为组件名称
    // eslint-disable-next-line consistent-return
    emitActions.search.forEach(el => {
      if (!Tsrv.utils.noValue(config[el.toID])) {
        el.toID = config[el.toID].config.name;
      } else {
        if (!el?.sourceToID) {
          this.$msg('-50007,FE,90'); // "这是旧版本的数据，请重新配置所有组件关联"
          result.errmsg = '这是旧版本的数据，请重新配置所有组件关联';
          return result;
        }
        el.toID = el.sourceToID;
      }
    });
    // eslint-disable-next-line consistent-return
    emitActions.refresh.forEach(el => {
      if (!Tsrv.utils.noValue(config[el.toID])) {
        el.toID = config[el.toID].config.name;
      } else {
        if (!el?.sourceToID) {
          this.$msg('-50007,FE,90'); // "这是旧版本的数据，请重新配置所有组件关联"
          result.errmsg = '这是旧版本的数据，请重新配置所有组件关联';
          return result;
        }
        el.toID = el.sourceToID;
      }
    });
    return result;
  }

  // 为SERVER准备menu
  public configToMenu(config) {
    return {
      funName: config.config.name,
      navName: config.menu.nav,
      navID: config.menu.navID,
      group: config.menu.group,
      parent: config.menu.parent
    };
  }

  private processMany2one(m2o) {
    if (typeof m2o === 'string') {
      return m2o.split(':')[0];
    }
    if (typeof m2o === 'undefined') {
      return '';
    }
    if (typeof m2o === 'object') {
      let flag = true;
      for (let i = 0; i < m2o.fields.length; i++) {
        if (m2o.fields[i].isReturn === true) {
          flag = false;
          break;
        }
      }
      return flag ? m2o.toTable : '';
    }
    return '';
  }

  // 为SERVER准备funs
  private configToLibfieldUnit(
    type,
    name,
    comment,
    status,
    config,
    compConfig
  ) {
    const column = config.tableColumns || config.formColumns;
    const uni = config.uniqueIndexes || [];
    const index = config.indexes || [];
    const fields = [];
    column.forEach(el => {
      if (!el?.nodeData) {
        fields.push({
          libFieldID: el.fieldID,
          field: el.field,
          type: el.type,
          notNull: el.notNull,
          defValue: el.defValue,
          comment: el.comment,
          isDescription: el.isDes === undefined ? false : el.isDes,
          m2oFun: this.processMany2one(el.many2one),
          footerMethod: el.footerMethod === undefined ? null : el.footerMethod,
          selectionID: el.selectionID ? el.selectionID : null,
          subQuery: Tsrv.utils.isEmpty(el.subQuery) ? null : el.subQuery,
          isI18n: Tsrv.utils.get(el, 'formatter.type') === 'i18n',
          alias: el.comment,
          isFile:
            Tsrv.utils.get(el, 'formatter.type') === 'file' ||
            Tsrv.utils.get(el, 'formatter.type') === 'image',
          formatterType: Tsrv.utils.get(el, 'formatter.type')
        });
      }
    });
    // 处理索引
    const uniqueIndexes = [];
    const indexes = [];
    uni.forEach(el => {
      const c = {
        indexField: []
      };
      el.forEach(item => {
        c.indexField.push(item.id);
      });
      uniqueIndexes.push(c);
    });
    index.forEach(el => {
      const c = {
        indexField: []
      };
      el.forEach(item => {
        c.indexField.push(item.id);
      });
      indexes.push(c);
    });

    const cf = Tsrv.utils.clone(config, true);
    this.clearTableColumnByNodeData(cf);
    // 返回数据
    return {
      type: config.originComp === 'custom' ? 'custom' : type,
      name,
      comment,
      status,
      fields,
      uniqueIndexes,
      indexes,
      permissions: this.configToPermission(
        this.getCurrentCompType(type, name, compConfig),
        config
      ),
      // 原克隆功能用，统一改为config.refTable
      // refTable: config.originComp === 'custom' ? config.originName : null,
      refTable: config?.refTable ?? '',
      config: {
        name: type,
        config: this.clearTableColumnByNodeData(cf)
      }
    };
  }

  private clearTableColumnByNodeData(cf: any): any {
    if (Tsrv.utils.noValue(cf?.tableColumns)) {
      return cf;
    }
    if (!Array.isArray(cf)) {
      return cf;
    }
    return cf.filter(el => Tsrv.utils.noValue(el?.nodeData));
  }

  private getCurrentCompType(type, name, compConfig): string {
    if (type === COMP_BASE_TAB_NAME) {
      Object.keys(compConfig).forEach(key => {
        if (compConfig[key].config.name === name) {
          type = compConfig[key].name;
        }
      });
      return type;
    }
    return type;
  }

  private configToPermission(type, config): Array<any> {
    // 获取权限模板
    const temp = this.GT_command.Tsetup.getCompClass(type).getPermissionList(
      config
    );
    // temp中按钮权限来自于compToolbar.ts文件中的设置，没有考虑到实际功能面板用户设置按钮开-关，所以这里需要做处理（影响：一个功能的按钮权限完全取决于compToolbar.ts文件中的设置）

    // 工具栏的权限
    if (config.toolbar) {
      // 标准按钮权限
      if (!Tsrv.noValue(config.toolbar)) {
        const toolbar = config.toolbar;
        const toolbarKeys = Object.keys(toolbar);
        const buttons = Tsrv.utils.find(
          temp,
          el => el.area === 'toolbar' && el.type === 'button'
        );
        buttons.data = buttons.data.filter(
          el =>
            toolbarKeys.indexOf(el) === -1 ||
            (toolbarKeys.indexOf(el) > -1 && toolbar[el])
        );
        buttons.data.indexOf('addon') > -1 &&
          buttons.data.splice(buttons.data.indexOf('addon'), 1);
      }
      // 自定义按钮权限 （此部分在组件配置类中有此操作）
      // if (config.toolbar.addon && config.toolbar.addon.length > 0) {
      //   const arr = [];
      //   config.toolbar.addon.forEach(el => {
      //     arr.push(el.title);
      //     if (el.children.length > 0) {
      //       el.children.forEach(child => {
      //         arr.push(child.title);
      //       });
      //     }
      //   });
      //   const find = Tsrv.utils.find(
      //     temp,
      //     el => el.area === 'toolbar' && el.type === 'customButton'
      //   );
      //   if (find) {
      //     find.data = find.data.concat(arr);
      //   }
      // }
    }
    if (config.selbill && config.selbill.length > 0) {
      const arr = [];
      config.selbill.forEach(el => {
        arr.push(el.id);
      });
      const find = Tsrv.utils.find(
        temp,
        el => el.area === 'toolbar' && el.type === 'selbill'
      );
      if (find) {
        find.data = find.data.concat(arr);
      }
    }
    // basegrid列权限
    if (config.tableColumns) {
      const tablearr = [];
      config.tableColumns.forEach(el => {
        tablearr.push(el.field);
      });
      const tablefind = Tsrv.utils.find(temp, el => el.area === 'table');
      if (tablefind) {
        tablefind.data = tablefind.data.concat(tablearr);
      }
    }

    // cbaseform列权限
    if (
      type === COMP_CBASE_FORM_NAME &&
      config.formPanels &&
      config.formPanels[0].content &&
      config.formPanels[0].content.formColumns
    ) {
      const formarr = [];
      config.formPanels[0].content.formColumns.forEach(el => {
        formarr.push(el.field);
      });
      const formfind = Tsrv.utils.find(temp, el => el.area === 'form');

      if (formfind) {
        formfind.data = formfind.data.concat(formarr);
      }
    }

    // basepanel列权限
    if (
      type === COMP_BASE_PANEL_NAME &&
      config.formPanels &&
      config.formPanels[0].content &&
      config.formPanels[0].content.formColumns
    ) {
      const formarr = [];
      config.formPanels[0].content.formColumns.forEach(el => {
        formarr.push(el.field);
      });
      const formfind = Tsrv.utils.find(temp, el => el.area === 'form');

      if (formfind) {
        formfind.data = formfind.data.concat(formarr);
      }
    }

    return temp;
  }

  // 为SERVER准备fun
  public configToLibfield(config, compConfig, libfieldConfig) {
    const name = config.name;
    const res = [];
    const cfg = config.config;

    cfg.name = cfg.name ? cfg.name.trim() : '';
    if (cfg.status === 'main') {
      this.mainLayoutName = cfg.name;
    }
    if (name === COMP_BASE_GRID_NAME) {
      res.push(
        this.configToLibfieldUnit(
          name,
          cfg.name,
          cfg.comment,
          cfg.status,
          cfg,
          compConfig
        )
      );
    } else if (name === COMP_REF_BASE_GRID_NAME) {
      res.push(
        this.configToLibfieldUnit(
          name,
          cfg.name,
          cfg.comment,
          cfg.status,
          cfg,
          compConfig
        )
      );
    } else if (name === COMP_CBASE_FORM_NAME) {
      const forms = cfg.formPanels;
      const formsRes = {
        type: cfg.originComp === 'custom' ? 'custom' : name,
        name: cfg.name,
        comment: cfg.comment,
        status: cfg.status,
        fields: [],
        uniqueIndexes: [],
        indexes: [],
        permissions: this.configToPermission(name, cfg),
        refTable: cfg.originComp === 'custom' ? cfg.originName : null,
        config: {
          name: COMP_CBASE_FORM_NAME,
          config: cfg
        }
      };
      forms.forEach(el => {
        const group = this.configToLibfieldUnit(
          name,
          cfg.name,
          cfg.comment,
          cfg.status,
          el.content,
          this.configStore.all
        );
        formsRes.fields = formsRes.fields.concat(group.fields);
        formsRes.uniqueIndexes = formsRes.uniqueIndexes.concat(
          group.uniqueIndexes
        );
        formsRes.indexes = formsRes.indexes.concat(group.indexes);
      });
      res.push(formsRes);
    } else if (name === COMP_BASE_PANEL_NAME) {
      const forms = cfg.formPanels;
      const tables = cfg.tablePanels;
      const formsRes = {
        type: cfg.originComp === 'custom' ? 'custom' : name,
        name: cfg.name,
        comment: cfg.comment,
        status: cfg.status,
        fields: [],
        uniqueIndexes: [],
        indexes: [],
        permissions: this.configToPermission(name, cfg),
        refTable: cfg.originComp === 'custom' ? cfg.originName : null,
        config: {
          name: COMP_BASE_PANEL_NAME,
          config: cfg
        }
      };
      forms.forEach(el => {
        const group = this.configToLibfieldUnit(
          name,
          cfg.name,
          cfg.comment,
          cfg.status,
          el.content,
          this.configStore.all
        );
        formsRes.fields = formsRes.fields.concat(group.fields);
        formsRes.uniqueIndexes = formsRes.uniqueIndexes.concat(
          group.uniqueIndexes
        );
        formsRes.indexes = formsRes.indexes.concat(group.indexes);
      });
      res.push(formsRes);
      tables.forEach(el => {
        const group = this.configToLibfieldUnit(
          name,
          el.label,
          el.label,
          '',
          el.content,
          this.configStore.all
        );
        res.push(group);
      });
      // }
      //  else if (name === 'basereport') {
      //   res.push({
      //     type: config.originComp === 'custom' ? 'custom' : name,
      //     name: cfg.name,
      //     comment: cfg.comment,
      //     status: cfg.status,
      //     fields: [],
      //     uniqueIndexes: [],
      //     indexes: [],
      //     permissions: this.configToPermission(name, cfg),
      //     refTable: null,
      //     config: {
      //       name,
      //       config: cfg
      //     }
      //   });
    } else if (name === COMP_GRAPH_NAME) {
      res.push(
        this.configToLibfieldUnit(
          name,
          cfg.name,
          cfg.comment,
          cfg.status,
          cfg,
          compConfig
        )
      );
    } else if (name === COMP_REF_GRAPH_NAME) {
      res.push(
        this.configToLibfieldUnit(
          name,
          cfg.name,
          cfg.comment,
          cfg.status,
          cfg,
          compConfig
        )
      );
    } else if (name === COMP_BASE_VIEW_NAME) {
      res.push(
        this.configToLibfieldUnit(
          name,
          cfg.name,
          cfg.comment,
          cfg.status,
          cfg,
          compConfig
        )
      );
    } else if (name === COMP_BASE_TAB_NAME) {
      this.configToLibfieldBaseTab(config, libfieldConfig, res, compConfig);
    } else if (name === COMP_REF_COMP_NAME) {
      res.push({
        type: config.originComp === 'custom' ? 'custom' : name,
        name: cfg.name,
        comment: cfg.comment,
        status: cfg.status,
        refTable: null,
        config: {
          name,
          config: cfg
        }
      });
    } else if (name === COMP_MULTI_FORM_NAME) {
      res.push({
        type: config.originComp === 'custom' ? 'custom' : name,
        name: cfg.name,
        comment: cfg.comment,
        status: cfg.status,
        refTable: null,
        permissions: this.configToPermission(name, cfg),
        config: {
          name,
          config: cfg
        }
      });
    } else if (name === COMP_DATE_NAV_NAME) {
      res.push({
        type: config.originComp === 'custom' ? 'custom' : name,
        name: cfg.name,
        comment: cfg.comment,
        status: cfg.status,
        refTable: null,
        config: {
          name,
          config: cfg
        }
      });
    } else if (name === COMP_REPORT_CHART_NAME) {
      res.push({
        type: config.originComp === 'custom' ? 'custom' : name,
        name: cfg.name,
        comment: cfg.comment,
        status: cfg.status,
        fields: [],
        refTable: null,
        permissions: this.configToPermission(name, cfg),
        config: {
          name,
          config: cfg
        }
      });
    } else if (name === COMP_CLONE_BASE_PANEL_NAME) {
      const forms = cfg.formPanels;
      const tables = cfg.tablePanels;
      const formsRes = {
        type: name,
        name: cfg.name,
        comment: cfg.comment,
        status: cfg.status,
        fields: [],
        uniqueIndexes: [],
        indexes: [],
        permissions: this.configToPermission(name, cfg),
        refTable: Tsrv.noValue(cfg?.refTable) ? null : cfg.refTable,
        config: {
          name: COMP_CLONE_BASE_PANEL_NAME,
          config: cfg
        }
      };
      forms.forEach(el => {
        const group = this.configToLibfieldUnit(
          name,
          cfg.name,
          cfg.comment,
          cfg.status,
          el.content,
          this.configStore.all
        );
        formsRes.fields = formsRes.fields.concat(group.fields);
        formsRes.uniqueIndexes = formsRes.uniqueIndexes.concat(
          group.uniqueIndexes
        );
        formsRes.indexes = formsRes.indexes.concat(group.indexes);
      });
      res.push(formsRes);
      tables.forEach(el => {
        const group = this.configToLibfieldUnit(
          name,
          el.label,
          el.label,
          '',
          el.content,
          this.configStore.all
        );
        res.push(group);
      });
    } else if (name !== COMP_TREE_GRID_NAME) {
      res.push({
        type: cfg.originComp === 'custom' ? 'custom' : name,
        name: cfg.name,
        status: cfg.status,
        fields: [],
        uniqueIndexes: [],
        indexes: [],
        permissions: this.configToPermission(name, cfg),
        refTable: null,
        config: {
          name,
          config: cfg
        }
      });
    }
    return res;
  }

  private configToLibfieldBaseTab(config, libfieldConfig, res, compConfig) {
    const handleSet = (compName: string, compCf: any) => {
      if (!Tsrv.utils.noValue(compCf)) {
        const cfg1 = compCf;
        cfg1.name = cfg1.name.trim();
        const find = Tsrv.utils.find(
          libfieldConfig,
          item => item.name === cfg1.name
        );
        if (compName === COMP_REF_COMP_NAME) {
          res.push({
            type: COMP_REF_COMP_NAME,
            name: cfg1.name,
            comment: cfg1.comment,
            status: cfg1.status,
            refTable: null,
            config: {
              name: cfg1.name,
              config: cfg1
            }
          });
        } else if (
          compName === COMP_BASE_GRID_NAME ||
          compName === COMP_REF_BASE_GRID_NAME
        ) {
          if (Tsrv.utils.noValue(find)) {
            res.push(
              this.configToLibfieldUnit(
                compName,
                cfg1.name,
                cfg1.comment,
                cfg1.status,
                cfg1,
                compConfig
              )
            );
          }
        } else if (compName === COMP_BASE_VIEW_NAME) {
          if (Tsrv.utils.noValue(find)) {
            const resData = this.configToLibfieldUnit(
              compName,
              cfg1.name,
              cfg1.comment,
              cfg1.status,
              cfg1,
              compConfig
            );
            resData.type = compName;
            resData.config.name = compName;
            res.push(resData);
          }
        } else {
          throw new Error('未定义的标签内组件类型');
        }
      }
    };
    // p = first|second
    const handle = p => {
      if (p.config.name === COMP_BASE_TAB_NAME) {
        this.configToLibfieldBaseTab(p.config, libfieldConfig, res, compConfig);
      } else {
        handleSet(p.config.name, p.config.config);
      }
    };
    config.config.panels.forEach(panel => {
      if (!Tsrv.noValue(panel.content?.config?.first)) {
        handle(panel.content.config.first);
        handle(panel.content.config.second);
      } else {
        handleSet(panel.content.name, panel.content.config);
      }
    });
  }

  /**
   * 配置数据恢复
   */
  // 递归拆解layout
  private takeLayoutApart(config) {
    if (config.type === 'layout') {
      this.takeLayoutApart(config.config.first);
      this.takeLayoutApart(config.config.second);
    } else {
      this.recoverConfig[config.cellID] = config;
    }
  }

  // config解析成配置
  private releaseFromConfig(config) {
    // 获取layout信息
    const layoutID = config.layoutID;
    this.layout.id = layoutID;
    this.layout.isSelected = true; // 设置layout选中状态

    // 设置当前layout
    this.layouts.forEach(el => {
      if (+el.layoutID === +layoutID) {
        this.layout.current = el.layoutConfig;
        // this.layout.current.config.value = config.config.value;
        this.syncLayoutValues(config.config, this.layout.current.config);
      }
    });

    // 准备拆解layout
    this.recoverConfig = {};
    // 拆解layout到recoverConfig
    this.takeLayoutApart(config);

    // 进一步拆解tabs
    for (const key in this.recoverConfig) {
      this.recoverTabs(key);
    }
    // 设置全局config
    this.GT_command.Tsetup.configStore.all = Tsrv.utils.clone(
      this.recoverConfig,
      true
    );
    this.configStore.all = Tsrv.utils.clone(this.recoverConfig, true);
    Tsrv.utils.clear(this.recoverConfig); // 清空中间变量
    // 设置editor
    this.$nextTick(() => {
      this.requestReloadView();
    });
  }

  private recoverTabs(key) {
    if (this.recoverConfig[key].name === COMP_BASE_TAB_NAME) {
      const cellID = this.recoverConfig[key].cellID; // 获取cellID
      // 拿出tabs里的组件
      this.recoverConfig[key].config.panels.forEach((el, idx) => {
        // tabs是容器
        if (
          el.content.type === 'layout' ||
          !Tsrv.noValue(el.content?.config?.first)
        ) {
          this.recoverTabsContainer(el.content); // 解析Tabs为容器的内容的所有组件
        } else {
          // 兼顾老版程序
          this.recoverConfig[`${cellID}-${idx + 1}`] = Tsrv.utils.clone(
            el.content,
            true
          );
          el.content = `${cellID}-${idx + 1}`; // 将tabs中组件设为序号
        }
      });
    }
  }

  /**
   * 同步layout中的split值
   */
  private syncLayoutValues(source: any, target: any) {
    // 递归终止条件
    if (
      Tsrv.noValue(source) ||
      Tsrv.noValue(target) ||
      typeof source !== 'object' ||
      typeof target !== 'object'
    ) {
      target.value = source.value;
      return;
    }
    // if (
    //   Object.prototype.hasOwnProperty.call(source, 'value') &&
    //   Object.prototype.hasOwnProperty.call(target, 'value')
    // ) {
    //   target.value = source.value;
    // }
    if (Object.prototype.hasOwnProperty.call(target, 'mode')) {
      target.value = source.value;
      const first = target['first'];
      const second = target['second'];
      if (!Tsrv.noValue(first) && first['type'] === 'layout') {
        this.syncLayoutValues(source['first']['config'], first['config']);
      }
      if (!Tsrv.noValue(second) && second['type'] === 'layout') {
        this.syncLayoutValues(source['second']['config'], second['config']);
      }
    }
  }

  private recoverTabsContainer(tabContent: any) {
    const handleSubTab = p => {
      if (!Tsrv.noValue(p.config.panels)) {
        p.config.panels.forEach(panel => {
          if (!Tsrv.noValue(panel.content?.config?.first)) {
            this.recoverTabsContainer(panel.content);
          } else {
            // eslint-disable-next-line no-use-before-define
            setRecoverForComp({
              moduleID: panel.content.moduleID,
              cellID: panel.content.cellID,
              name: panel.content.name,
              type: panel.content.type,
              config: panel.content
            });
            panel.content.config = {};
          }
        });
      }
    };
    const setRecoverForComp = (p: any) => {
      if (p.name === COMP_BASE_TAB_NAME) {
        handleSubTab(p.config);
        this.recoverConfig[p.cellID] = Tsrv.utils.clone(p.config, true);
      } else {
        this.recoverConfig[p.cellID] = Tsrv.utils.clone(p.config, true);
        p.config = null;
      }
      p.name = p.cellID; // 还原容器cellID表达，保存时会变成组件名
    };

    const first = tabContent?.config?.first;
    if (!Tsrv.noValue(first)) {
      setRecoverForComp(first);
    }
    const second = tabContent?.config?.second;
    if (!Tsrv.noValue(second)) {
      setRecoverForComp(second);
    }
  }

  private findLayoutInit(): Promise<boolean> {
    const gpd = new ts.T_getPartDataArg();
    gpd
      .setFunName(Tsrv.globalVar.funName.SYS_LAYOUT)
      .setLang(Tsrv.globalVar.i18n.zhCN);
    gpd.setPage(1, 100).setDbType(Tsrv.globalVar.dbType.MYSQL);
    gpd.setGlobalSearch('');
    gpd.setFilter({});
    gpd.setSort([
      {
        field: Tsrv.globalVar.fieldName.fNumber,
        // field: 'f_number',
        value: tvs.V_sortValue.ASC
      } as ifs.I_sort
    ]);
    return new Promise((resole, reject) => {
      Tsrv.getData<any, ifs.I_getPartDataArg>(gpd)
        .then(([b, res, rows, msg]) => {
          if (b) {
            this.layouts = [];
            rows.forEach((el: any) => {
              el[Tsrv.globalVar.fieldName.funConfig] = Tsrv.utils.toStringJSON(
                el.fun_config
              );
              this.layouts.push(el.fun_config);
            });
            resole(true);
          } else {
            resole(false);
          }
        })
        .catch(r => {
          reject(false);
        });
    });
  }

  /**
   * 常用函数
   */
  // 保存当前编辑comp到总体comp config
  private saveChanges(update = false) {
    const cellID = this.cell.id;
    if (cellID !== '') {
      this.configStore.all[cellID] = this.GT_command.Tsetup.component.current;
      this.component.current = this.GT_command.Tsetup.component.current;
      this.GT_command.Tsetup.configStore.all[
        cellID
      ] = this.GT_command.Tsetup.component.current;
    }
    if (update) {
      this.status.isChange = !this.status.isChange;
      this.$nextTick(() => {
        this.requestReloadView();
      });
    }
  }

  // 重新加载显示视图
  private requestReloadView() {
    const comps = this.GT_compBox.getCompsRegexMathName(
      `^${this.G_compID}_LayoutEditor`
    );
    if (!Tsrv.noValue(comps)) {
      comps.forEach(el => {
        el.comp.processCompConfig(this.configStore.all);
      });
    }
  }

  /*
    /* 重置信息
    */
  private resetAllDataDefault(stored = true) {
    this.GT_command.Tsetup.clear(stored);
    this.switcher.main = 1;
    this.switcher.attr = 'attr_0';
    this.layout.id = '';
    this.layout.isSelected = false;
    this.layout.current = null;
    this.cell.id = '';
    this.cell.isSelected = false;
    this.component.current = null;
    Tsrv.utils.clear(this.configStore.all);
    if (stored) this.configStore.stored = [];
    this.status.isChange = !this.status.isChange;
    this.requestReloadView();
  }

  /**
   * Current 相关
   */
  // 设置主组件
  private setMainComponent(val) {
    for (const key in this.configStore.all) {
      this.configStore.all[key].config['status'] = '';
    }
    if (val) {
      this.component.current.config['status'] = 'main';
    }
    this.saveChanges();
  }

  /*
    /* cell 控制
    */

  // 放入组件
  private handleCellFilled(type, componentID, cellID) {
    if (type === 'new') {
      // 新的组件，从列表中查找配置信息
      if (this.configStore.all[cellID]) {
        const configClone = Tsrv.utils.clone(
          this.configStore.all[cellID],
          true
        );
        this.configStore.stored.unshift(configClone);
        this.GT_command.Tsetup.configStore.stored.unshift(configClone);
      }
      // 如果有上次未配置的layout则删除
      const keys = Object.keys(this.configStore.all);
      for (const key of keys) {
        if (key === 'undefined') {
          if (this.configStore.all[key]?.name === cellID) {
            delete this.configStore.all[key];
          }
        }
      }
      this.addConfigStrore(componentID, cellID);
    } else if (type === 'stored') {
      this.configStore.all[cellID] = Tsrv.utils.clone(
        this.configStore.stored[componentID],
        true
      );
      this.GT_command.Tsetup.configStore.all[cellID] = this.configStore.all[
        cellID
      ];
    }
    this.status.isChange = !this.status.isChange;
  }

  public addConfigStrore(
    componentID: string,
    cellID: string,
    compName: N_oSetupPanel.N_comps.COMP_NAME = null
  ) {
    this.GT_command.Tsetup.component.list.forEach(ele => {
      const el = Tsrv.utils.clone(ele, true);
      if (Tsrv.utils.noValue(compName)) {
        if (+el.moduleID === +componentID) {
          this.configStore.all[cellID] = el;
          this.GT_command.Tsetup.configStore.all[cellID] = el;
        }
      } else {
        if (el.name === compName) {
          this.configStore.all[cellID] = el;
          this.GT_command.Tsetup.configStore.all[cellID] = el;
        }
      }
    });
  }

  // 组件位点击
  private handleCellSelected(cellID) {
    this.cell.isSelected = true; // 设置cell选择状态
    this.switcher.main = 2; // 默认切换到组件面板
    this.switcher.attr = 'attr_0'; // 属性面板切换到组件
    // 如果该位置没有添加compo，显示选择组件
    if (!this.configStore.all[cellID]) {
      this.component.current = null;
      this.GT_command.Tsetup.component.current = null;
      this.cell.id = '';
    } else {
      this.component.current = Tsrv.utils.clone(
        this.configStore.all[cellID],
        true
      );
      this.GT_command.Tsetup.component.current = Tsrv.utils.clone(
        this.configStore.all[cellID],
        true
      );
      this.cell.id = cellID;
      this.GT_command.Tsetup.cell.id = cellID;

      this.status.isChange = !this.status.isChange;
    }
    this.GT_command.setToolbarState({
      name: VtoolbarState.SAVE,
      state: false
    });
  }

  // 删除当前组件位已选择的组件
  private handleResetCurrentCell() {
    const cellID = this.cell.id;

    // 如果当前组件存在
    if (this.configStore.all[cellID]) {
      // 删除当前组件
      delete this.configStore.all[cellID];
      // 删除当前组件子组件
      for (const key in this.configStore.all) {
        if (key.indexOf(`${cellID}-`) >= 0) {
          delete this.configStore.all[key];
        }
      }
      this.component.current = null;
      this.GT_command.Tsetup.component.current = null;
      this.cell.id = '';
      // 发送配置给其他配件
      this.requestReloadView();
    }
  }

  /* 
    /* layout 控制
    */

  // layout切换
  private layoutChange(config) {
    // 是否已选择layout           //如果是新增模板就不需要插历史组件clr
    if (this.layout.isSelected && !this.layoutisadd) {
      // 插入历史组件
      const configCopy = Tsrv.utils.clone(this.configStore.all, true);
      for (const key in configCopy) {
        if (configCopy[key].name !== COMP_BASE_TAB_NAME) {
          delete configCopy[key]['layoutID'];
          this.configStore.stored.unshift(configCopy[key]);
          this.GT_command.Tsetup.configStore.stored.unshift(configCopy[key]);
        }
      }
      this.resetAllDataDefault(false);
    }

    this.layout.id = config.layoutID; // 设置当前layoutID
    this.layout.isSelected = true; // 切换layout选择状态
    this.layout.current = { layoutID: 0, name: '1', type: 'comp', config: {} }; // 初始化缓冲加载
    setTimeout(() => {
      this.layout.current = config; // 设置当前layoutConfig
      this.status.isChange = !this.status.isChange;
      if (this.layoutisadd) {
        this.editorconfig = config; // 如果是添加模板就赋给editorconfig，由它去设计
      }
    }, 50);
  }

  private findConfig() {
    // return this.layout.current;   //clr--2305--modify
    return this.editorconfig;
  }

  private setConfig(confignew) {
    this.layout.current = confignew;
  }
  /* 
    /* 基础控制 
    */

  // 切换属性
  private handleAttrClick(index) {
    this.switcher.attr = index;
  }

  // 切换左边栏
  private handleMainSwitchChange(index) {
    const currentIndex = this.switcher.main;
    switch (index) {
      case 1:
        if (index === +currentIndex) {
          this.switcher.main = 0;
        } else {
          this.switcher.main = 1;
        }
        break;
      case 2:
        if (this.layout.isSelected) {
          if (index === +currentIndex) {
            this.switcher.main = 0;
          } else {
            this.switcher.main = 2;
          }
        }
        break;

      default:
        break;
    }
  }

  private init(config) {
    // 新建
    if (Tsrv.noValue(config)) {
      this.resetAllDataDefault();
    } else {
      this.resetAllDataDefault();
      this.releaseFromConfig(config);
    }
  }

  // 打开setup界面
  private show(config) {
    const that = this;
    this.showAnimation = {
      display: 'block',
      // 'z-index': 102 + this.id
      'z-index': 202 + this.id
    };
    this.status.isShow = true;
    this.init(config); // 初始化
    setTimeout(() => {
      that.showAnimation = {
        display: 'block',
        opacity: 1,
        // 'z-index': 102 + this.id
        'z-index': 202 + this.id
      };
    }, 10);
  }

  // 关闭setup界面
  private close() {
    const that = this;
    this.showAnimation = {
      display: 'block',
      opacity: 0,
      // 'z-index': 102 + this.id
      'z-index': 202 + this.id
    };
    setTimeout(() => {
      that.showAnimation = {
        display: 'none',
        // 'z-index': 102 + this.id
        'z-index': 202 + this.id
      };
      that.resetAllDataDefault();
      this.$bus.$emit(`modal_${this.id}`, null);
    }, 200);
  }

  // 保存分支compBoxInfo
  public globalContextMenu(p: ifs.I_compBoxInfo) {
    this.GT_command.setGlobalContextMenuCompBoxInfo(p);
  }

  public mainTopCompBoxInfo(p: ifs.I_compBoxInfo) {
    this.GT_command.setMinTopCardBoxCompBoxInfo(p);
  }

  public onContextSelect(e) {
    if (e === 'null') {
      this.GT_command.closeGcontextMenu();
    } else {
      this.GT_command.onContextSelect(e);
      this.GT_command.closeGcontextMenu();
    }
  }

  // 拖拽的事件处理
  public allowDrop(e) {
    if (e.dataTransfer.types.includes('layout_change')) {
      e.dataTransfer.dropEffect = 'copy';
    } else {
      if (!this.layout.isSelected) {
        e.dataTransfer.dropEffect = 'none';
      }
    }
    e.preventDefault();
  }

  public dragEnter(e) {
    e.currentTarget.style.background = 'rgba(245, 245, 245, 0.5)';
  }

  public dragLeave(e) {
    e.currentTarget.style.background = '#fff';
  }

  public compDrop(e) {
    e.stopPropagation(); // 阻止事件冒泡到B
    this.GT_command.setToolbarState(
      {
        name: VtoolbarState.NEW,
        state: true
      },
      {
        name: VtoolbarState.SAVE,
        state: false
      }
    );
    // this.layoutisadd = false; // 直接拖拉的不是可以编辑的
    const idx: number = e.dataTransfer.getData('layout_change');
    this.GT_compBox.getCompMergeName(
      this.G_compID,
      'LayoutsPanel'
    ).layoutDoChange(idx);
    e.currentTarget.style.background = '#fff';
    if (e.dataTransfer.types[0] === 'layout_change') {
      this.layoutisadd = false;
    }
  }

  onDragStart(e) {
    e.stopPropagation(); // 阻止事件冒泡
  }

  public btnClick(item, e) {
    switch (item.key) {
      case 'new':
        // layouts不能拖
        this.GT_command.setlayoutsPanelDraggable(false);
        // 组件不能拖
        this.GT_command.setCompPanelDraggable(false);
        // 保存可用
        this.GT_command.setToolbarState(
          {
            name: VtoolbarState.NEW,
            state: true
          },
          {
            name: VtoolbarState.SAVE,
            state: false
          }
        );

        this.layoutisadd = true;
        // eslint-disable-next-line no-case-declarations
        const newConfig = {
          layoutID: 0,
          name: '1',
          type: 'comp',
          config: {}
        };
        this.layoutChange(newConfig);
        break;
      case 'save':
        if (this.layoutisadd) {
          this.GT_command.setToolbarState({
            name: VtoolbarState.NEW,
            state: false
          });
          this.doLayoutSave();
        } else {
          this.save();
        }
        break;
      case 'delete':
        this.GT_command.setToolbarState({
          name: VtoolbarState.DELETE,
          state: true
        });
        this.GT_compBox.getCompMergeName(
          this.G_compID,
          'LayoutsPanel'
        ).delModel();
        break;

      default:
        break;
    }
  }

  public doLayoutSave() {
    this.$Modal.confirm({
      title: '是否新增布局模板?',
      onOk: () => {
        const n: number = this.GT_compBox.getCompMergeName(
          this.G_compID,
          'LayoutsPanel'
        ).arrayLength(); // 找出LayoutsPanel中的数组的长度
        const TinsRec = new ts.T_ctdInsRec().setScope({});
        const Trule = new ts.T_rule();
        const vs: ifs.I_fv[] = [];
        const a: ifs.I_fv = new ts.T_fv();
        const a1: ifs.I_fv = new ts.T_fv();
        const a2: ifs.I_fv = new ts.T_fv();
        // a.setField('_sys_fun_id').setValue(n);
        a.setField('f_number').setValue(n);
        a2.setField('title').setValue(n);
        const layoutConfigStr = {
          layoutID: String(n),
          title: 'oSetupPanel.ts.freefile', // 自由组合
          detail: 'oSetupPanel.ts.fresform', // 自由组合
          layoutConfig: this.layout.current
        }; // 组合成对象
        layoutConfigStr.layoutConfig.layoutID = n;
        // this.GT_compBox.getCompMergeName(
        //   this.G_compID,
        //   'LayoutsPanel'
        // ).arrayAdd(layoutConfigStr); // 添加到LayoutsPanel
        const layoutConfigStr1 = JSON.stringify(layoutConfigStr);
        a1.setField('fun_config').setValue(layoutConfigStr1);
        TinsRec.setBaseDataGeneral(Trule, {}, a, a1, a2);
        // TinsRec.setBaseDataGeneral(Trule, {}, a, a1);
        // return new Promise((resolve, reject) => {
        Tsrv.addRows('_sys_layout', 'mysql', Trule, TinsRec).then(res => {
          const [b, r, err] = res;
          if (!err) {
            this.GT_compBox.getCompMergeName(
              this.G_compID,
              'LayoutsPanel'
            ).arrayAdd(layoutConfigStr); // 添加到LayoutsPanel
          }
          // 保存状态
          // 此处可打包到指挥部形成doLayout状态模板，可减少代码量
          this.GT_command.setToolbarState({
            name: VtoolbarState.NEW,
            state: false
          });
          this.GT_command.clearLayout();
          this.GT_command.setlayoutsPanelDraggable(true);
          this.GT_command.setCompPanelDraggable(true);
          this.GT_compBox.getCompMergeName(
            this.G_compID,
            'LayoutsPanel'
          ).layoutInit();

          // resolve(r);
        });
        // })
      },
      onCancel: () => {
        // 保存不可用
        this.GT_command.setToolbarState({
          name: VtoolbarState.NEW,
          state: false
        });
        this.GT_command.clearLayout();
        this.GT_command.setlayoutsPanelDraggable(true);
        this.GT_command.setCompPanelDraggable(true);
      }
    });
  }

  public upLayoutfvalue() {
    const TinsUpd = new ts.T_ctdUpdRec();
    const Trule = new ts.T_rule();
    const a: ifs.I_fv = new ts.T_fv();
    const Tfilter = new ts.T_filter();
    const n: number = this.layout.current.layoutID;
    const syslayoutidvalue = this.GT_compBox.getCompMergeName(
      this.G_compID,
      'LayoutsPanel'
    ).layoutIdvalued(n); // 找出_sys_fun表中 sys_id和f_value 的值更新
    const f_value: number = syslayoutidvalue.f_value + 1;
    const sysfunid: string = syslayoutidvalue.sysfunid;
    const id: any = syslayoutidvalue.id;
    a.setField('f_value').setValue(f_value);
    TinsUpd.setData({}, Tfilter.parseEQ('id', id), Trule, a);
    Tsrv.updRows('_sys_layout', 'mysql', Trule, TinsUpd).then(res => {
      const [b, r, _] = res;
    });
  }

  private getTabsSubContent(obj: any, config: any) {
    const findCell = (pKey: string): string[] => {
      const regEx = new RegExp(`^${pKey}-`);
      return Object.keys(config)
        .filter(k => RegExp(k))
        .sort();
    };
    const makeContent = (pKey: string): any => {
      const res = {};
      const keys = findCell(pKey);
      if (keys.length <= 0) {
        return null;
      }
      if (keys.length === 1) {
        if (Tsrv.utils.noValue(findCell(keys[0]))) {
          res['first'] = config(keys[0]);
        } else {
          res['first'] = makeContent(keys[0]);
        }
      } else {
        if (Tsrv.utils.noValue(findCell(keys[0]))) {
          res['first'] = config(keys[0]);
        } else {
          res['first'] = makeContent(keys[0]);
        }
        if (Tsrv.utils.noValue(findCell(keys[1]))) {
          res['second'] = config(keys[1]);
        } else {
          res['second'] = makeContent(keys[1]);
        }
      }
      return res;
    };

    obj.content = {
      layoutID: 1,
      name: 'layout',
      type: 'layout',
      config: makeContent(obj.content[0]) // 从头开始组装二叉树
    };
  }
}
</script>
<style lang="scss" scoped>
//框架
.setup {
  position: absolute;
  width: 100%;
  height: 100%;
  left: 0;
  top: 0;
  opacity: 0;
  display: none;
  transition: ease-in-out 0.2s;
  .back {
    background-color: rgba(0, 0, 0, 0.5);
    width: 100%;
    height: 100%;
  }
  .front {
    position: absolute;
    width: calc(100% - 30px);
    height: calc(100% - 30px);
    top: 15px;
    left: 15px;
    .box {
      width: 100%;
      height: 100%;
      background-color: #fff;
      display: flex;
      flex-direction: column;
      border-radius: var(--border-radius-model);
      overflow: hidden;
      .header {
        width: 100%;
        position: relative;
        height: 58px;
        background-color: rgba(244, 244, 244, 1);
        padding: 0 30px;
        box-shadow: var(--default-box-shadow-down);
        z-index: 11;
        .wytoolbar {
          width: 20%;
          padding-top: 8px;
          margin-left: 45%;
          background-color: rgba(244, 244, 244, 1);
          #wytoolbarid {
            border: none;
            box-shadow: none;
          }
        }
      }
      .main {
        width: 100%;
        flex: 1;
        overflow: hidden;
        display: flex;
        flex-direction: row;
        position: relative;
        .main-top {
          z-index: 1000;
        }
        .main-left {
          width: 200px;
          overflow: visible;
          //   box-shadow: rgba(0, 0, 0, 0.05) 1px 0px 1px;
          // border-right: 1px solid rgb(221, 221, 221);
          background-color: #fff;
          display: flex;
          flex-direction: column;
          z-index: 1;
        }
        .main-mid {
          position: relative;
          flex-grow: 1;
        }
        .main-right {
          width: 360px;
          box-shadow: rgba(0, 0, 0, 0.05) -1px 0px 1px;
          border-left: 1px solid rgb(221, 221, 221);
          background-color: #fff;
          z-index: 10;
          ::v-deep .ivu-tabs-tab {
            text-overflow: ellipsis;
            overflow: hidden;
            white-space: nowrap;
          }
        }
      }
    }
  }
}

//头部
.header {
  .save-btn {
    position: absolute;
    right: 73px;
    top: 16px;
    height: 26px;
    padding: 0 10px;
    border-radius: 3px;
    border: none;
    box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05);
  }
  .close-btn {
    position: absolute;
    right: 30px;
    top: 15px;
    width: 28px;
    height: 28px;
    cursor: pointer;
    color: #666;
    .ivu-icon {
      font-size: 28px;
      font-weight: bold;
    }
  }
  .check-btn {
    width: 50px;
    height: 56px;
    float: left;
    margin-right: 20px;
    .check-btn-icon {
      width: 46px;
      height: 28px;
      background-color: #fff;
      border-radius: 3px;
      text-align: center;
      line-height: 30px;
      margin: 8px 2px 0;
      cursor: pointer;
      box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05);
      .ivu-icon {
        font-size: 18px;
        color: #666;
      }
    }
    .check-btn-text {
      color: #666;
      display: block;
      text-align: center;
      font-size: 10px;
      line-height: 20px;
      font-weight: 500;
    }
    .active {
      background-color: var(--default-hover-font-color);
      .ivu-icon {
        color: #fff;
      }
    }
    .disabled {
      background-color: #e2e2e2;
      .ivu-icon {
        color: #989898;
      }
    }
  }
}

//右侧属性
.main-right ::v-deep {
  position: relative;
  //tabs的header
  .ivu-tabs-tab:nth-child(2) {
    width: 100px;
  }
  .ivu-tabs-tab:nth-child(3) {
    width: 100px;
  }
  .ivu-tabs-tab:nth-child(4) {
    width: 80px;
  }
  .ivu-tabs-tab:nth-child(5) {
    width: 79px;
  }
  //tabs的body
  .ivu-tabs {
    overflow: visible;
    height: 100%;

    .ivu-tabs-content {
      height: calc(100% - 48px);
      overflow: auto;
    }
    .ivu-tabs-content::-webkit-scrollbar {
      /* 滚动条的宽和高 */
      width: 8px;
      height: 0px;
    }
    .ivu-tabs-content::-webkit-scrollbar-thumb {
      /*滚动条里面小方块*/
      border-radius: 4px;
      box-shadow: inset 0 0 2px rgba(0, 0, 0, 0.2);
      background: rgba(0, 0, 0, 0.2);
    }
  }
}
.tab-pane-cover ::v-deep {
  width: 100%;
  display: flex;
  flex-direction: column;
  padding: 10px 15px;
  .divider-empty {
    width: 100%;
    height: 15px;
    flex-shrink: 0;
  }
  .divider-empty-thin {
    width: 100%;
    height: 5px;
    flex-shrink: 0;
  }
  .ivu-divider-horizontal {
    margin: 0;
    flex-shrink: 0;
  }
  .tab-box {
    position: relative;
    .title {
      font-weight: bold;
      height: 36px;
      display: block;
      line-height: 36px;
    }
    .empty {
      display: block;
      height: 30px;
      line-height: 30px;
      width: 100%;
      text-align: center;
      color: #999;
    }
  }
}
</style>
